package com.ndood.gateway.app.web.aspect;

import java.util.Date;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.ndood.gateway.app.security.rsa.RsaKeyPair;
import com.ndood.gateway.app.security.rsa.RsaLoader;
import com.ndood.gateway.app.security.rsa.RsaQuery;
import com.ndood.gateway.base.pojo.GatewayResultVo;
import com.ndood.gateway.base.utils.AesUtil;
import com.ndood.gateway.base.utils.RSAUtil;

/**
 * RSA加解密，签名切片 https://www.jianshu.com/p/b0923c27f302
 * https://blog.csdn.net/qgfjeahn/article/details/60144241
 */
@Component
@Aspect
public class RsaAop {

	@Autowired
	private RsaLoader rsaLoader;

	@Autowired
	private ObjectMapper objectMapper;

	// @Before(value = "execution(*
	// com.ndood.payment.controller.v*.*Controller.*(..)) &&
	// args(authId,sign,text,..)")
	// public void verify(String authId,String sign,SignText text){
	// String publicKey = rsaLoader.getPublicKey(authId);
	// Assert.isTrue(RSAUtil.verify(text.toText(),sign,publicKey),"验签失败");
	// }

	@Around(value = "execution(* com.ndood.payment.controller.v*.*.*(..)) && args(authId,query,..)")
    public GatewayResultVo verify(ProceedingJoinPoint point,String authId, RsaQuery query) throws Throwable {
        
    	// Step1: 获取authId对应的rsa密钥
		JavaType jvt = objectMapper.getTypeFactory().constructParametricType(Map.class,String.class,Object.class);
    	RsaKeyPair keyPair = rsaLoader.getKeyPairByAuthId(authId);	

    	try {
	    	
	    	// Step2: Rsa解密获取aesKey
			String encryptAesKey = query.getEncryptAesKey();    	
			String aesKey = RSAUtil.decrypt(keyPair.getPrivateKey(), encryptAesKey);
	
			// Step3: Aes解密获取decryptData
			String encryptData = query.getEncryptData();
	    	Map<String, Object> map = objectMapper.readValue(AesUtil.decrypt(encryptData, aesKey), jvt); 
	    	
	    	// Step5: 验证签名
			Long timestamp = (Long) map.get("timestamp");
			Long currentTimestamp = new Date().getTime() / 1000;
			Assert.isTrue((currentTimestamp - timestamp > 3600), "请求已超过1小时");
			String sign = (String) map.get("sign"); // 先获取sign保存
			boolean verify = RSAUtil.verify(map, sign, keyPair.getThirdPublicKey());
			Assert.isTrue(verify, "rsa验签失败");

	    	// Step6: 将参数注入requestBody，看看是否可行
			String json = (String) map.get("data");
			query.setAttrs(objectMapper.readValue(json,jvt));
			query.setEncryptAesKey(null);
			query.setEncryptData(null);
    	
		} catch (Exception e) {
			throw new IllegalArgumentException("请求RSA解密失败:"+e.getMessage(), e);
		}
    	
    	// Step7: 执行方法
    	GatewayResultVo result = (GatewayResultVo) point.proceed();
    	
    	try {
    		// Step8: 将data转换成map
    		Object data = result.getData();
    		if(data == null) { // 返回结果没有数据的直接返回
    			return result;
    		}

    		// Step9: 生成RSA签名，签名的时候需要加时间戳，保证时效性
    		String json = objectMapper.writeValueAsString(data);
    		Map<String,Object> map = Maps.newHashMap();
    		map.put("data", json); // json数据源串
    		map.put("timestamp", new Date().getTime() / 1000); // 时间戳，保证数据的时效性
    		String sign = RSAUtil.sign(map, keyPair.getPrivateKey()); // 用RSA生成签名
    		map.put("sign", sign);
    		
    		// Step10: Aes加密
    		String aesKey = AesUtil.randomAesKey();
    		String encryptData = AesUtil.encrypt(objectMapper.writeValueAsString(map), aesKey);
    		result.setEncryptData(encryptData);
    		result.setData(null);

    		// Step11: Rsa加密AesKey
			String encryptAesKey = RSAUtil.encrypt(keyPair.getThirdPublicKey(), aesKey);
			result.setEncryptAesKey(encryptAesKey);
    		
    	}  catch (Exception e) {
			throw new IllegalArgumentException("返回RSA加密失败:"+e.getMessage(), e);
		}
    	
    	return result;
    }
	
}
